// Determine if the bootloader or application should start
//
// Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

#include <string.h>         // strlen
#include "autoconf.h"       // CONFIG_*
#include "board/flash.h"    // flash_read_block
#include "board/gpio.h"     // gpio_in_setup
#include "board/misc.h"     // set_bootup_code
#include "bootentry.h"      // bootentry_check
#include "canboot.h"        // udelay
#include "ctr.h"            // DECL_CTR

// Generated by buildcommands.py
DECL_CTR("DECL_BUTTON " __stringify(CONFIG_BUTTON_PIN));
extern int32_t button_gpio, button_high, button_pullup;

// Check for a bootloader request via double tap of reset button
static int
check_button_pressed(void)
{
    if (!CONFIG_ENABLE_BUTTON)
        return 0;
    struct gpio_in button = gpio_in_setup(button_gpio, button_pullup);
    udelay(10);
    return gpio_in_read(button) == button_high;
}

#define DOUBLE_CLICK_MIN_US 10000
#define DOUBLE_CLICK_MAX_US 500000

// Check for a bootloader request via double tap of reset button
static int
check_double_reset(void)
{
    if (!CONFIG_ENABLE_DOUBLE_RESET)
        return 0;
    if (CONFIG_HAVE_BOARD_CHECK_DOUBLE_RESET)
        // Use board specific detection mechanism
        return board_check_double_reset();
    // Set request signature and delay - this enters the bootloader if
    // the reset button is double clicked
    udelay(DOUBLE_CLICK_MIN_US);
    set_bootup_code(REQUEST_CANBOOT);
    udelay(DOUBLE_CLICK_MAX_US - DOUBLE_CLICK_MIN_US);
    // No reset, clear the bootup code
    set_bootup_code(0);
    return 0;
}

// Check if bootloader or application should be started
int
bootentry_check(void)
{
    // Enter the bootloader in the following conditions:
    // - The request signature is set in memory (request from app)
    // - No application code is present
    uint64_t bootup_code = get_bootup_code();
    if (bootup_code == REQUEST_CANBOOT || !application_check_valid()
        || check_button_pressed()) {
        // Start bootloader main loop
        set_bootup_code(0);
        return 1;
    }
    return check_double_reset();
}
